///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007-2009 ILOG, S.A.
//  All Rights Reserved.
//  The following is ILOG Source Code.  No modifications may be made to the  
//  ILOG Source Code.  Any usage of the ILOG Source Code is subject to 
//  the terms and conditions of the ILOG End User License Agreement   
//  applicable to this ILOG software product.
//
///////////////////////////////////////////////////////////////////////////////

package ilog.gauges.gaugesClasses.circular
{
  import flash.geom.Point;
  
  import ilog.gauges.circular.CircularGauge;
  import ilog.gauges.gaugesClasses.GaugeElement;
  import ilog.utils.MathUtil;

  /**
   * The <code>CircularGaugeElement</code> class is the base class for visual elements
   * of circular gauges. This class defines layout properties which can all be 
   * expressed in percentages. The layout of the visual elements depends on gauge padding  
   * and aspectRatio properties. <code>CircularGauge</code> sets the size and position 
   * of all the visual elements to the same values. 
   * Then the elements are rendered according to their layout configuration defined 
   * by an origin point and a radius length expressed in percentages.
   * <p>
   * The origin point is controlled by the <code>originX</code> and <code>originY</code>
   * properties. These properties can be expressed in pixels or in a percentage 
   * of the total component size.
   * For example, <code>originX</code> and <code>originY</code> are set by default 
   * to <code>50%</code>, which corresponds to the center of the gauge. If <code>originY</code> 
   * is <code>90%</code>, the origin is near the bottom center like for semicircular gauges.
   * </p>
   * <p>
   * The radius of the element is controlled by the <code>radius</code> and 
   * <code>radiusRelativeTo</code> properties. Default values are <code>50%</code> 
   * and <code>"height"</code>, which means the radius length is equal to half 
   * the height of the element. Having <code>"width"</code> instead of <code>"height"</code>
   * for <code>radiusRelativeTo</code> would make the radius length equal to half
   * the width of the element.
   * </p>
   * 
   * @mxml
   *  
   * <p>The <code>&lt;ilog:CircularGaugeElement&gt;</code> tag inherits all the properties
   * of its parent classes and adds the following properties:</p>
   *  
   * <pre>
   * &lt;ilog:CircularGaugeElement
   *   <strong>Properties</strong>
   *   originX="50%"
   *   originY="50%"
   *   radius="50%"  
   *   radiusRelativeTo="height"
   *  /&gt;
   * </pre>
   * 
   * @see ilog.gauges.circular.CircularGauge
   */  
  public class CircularGaugeElement extends GaugeElement
  {

    /**
     *  Constructor.
     */          
    public function CircularGaugeElement()
    {
      super();
    }

    /**
     * @private
     */
    override protected function measure():void
    {
      super.measure();
      if (isNaN(originX) ||
          isNaN(originY) ||
          isNaN(radius))
      {
        measuredMinWidth = measuredMinHeight = measuredWidth = measuredHeight = 0;
      }
      else
      {
        measuredMinWidth = measuredWidth = originX + radius;
        measuredMinHeight = measuredHeight = originY + radius;
      }
    }
    
    /**
     * The <code>CircularGauge</code> that holds the gauge element.
     */
    public function get gauge():CircularGauge
    {
      return owner as CircularGauge;
    }    

    /**
     * Returns the origin point of the element, in pixel coordinates.
     * 
     * @return Origin point, in pixel coordinates.  
     */      
    public function getOrigin():Point
    {
      var ox:Number = ! isNaN(originX) ? originX : unscaledWidth * percentOriginX / 100;
      var oy:Number = ! isNaN(originY) ? originY : unscaledHeight * percentOriginY / 100;
      return new Point(ox, oy);
    }

    /**
     * Returns the radius length of the element, in pixels.
     *  
     * @return Radius length, in pixels.  
     */      
    public function getRadius():Number
    {
      if (isNaN(percentRadius))
        return radius;
      
      if (radiusRelativeTo == "height")
      {
        return unscaledHeight * percentRadius / 100;
      }
      else
      {
        return unscaledWidth * percentRadius / 100;
      }
    }

    /**
     *  @private
     */
    private var _percentOriginX:Number = 50;

    [Bindable("resize")]
    [Inspectable(environment="none")]

    /**
     * Number that specifies the x position of the origin as a percentage
     * of the element's width.
     *  
     * @default 50
     */
    public function get percentOriginX():Number
    {
      return _percentOriginX;
    }

    /**
     *  @private
     */
    public function set percentOriginX(value:Number):void
    {
      _percentOriginX = value;
      if (! isNaN(value))
        _originX = NaN;      
      invalidateDisplayList();
    }

    /**
     *  @private
     */
    private var _percentOriginY:Number = 50;

    [Bindable("resize")]
    [Inspectable(environment="none")]

    /**
     * Number that specifies the y position of the origin as a percentage
     * of the element's height. 
     * 
     * @default 50
     */
    public function get percentOriginY():Number
    {
      return _percentOriginY;
    }

    /**
     *  @private
     */
    public function set percentOriginY(value:Number):void
    {
      _percentOriginY = value;
      if (! isNaN(value))
        _originY = NaN;
      invalidateDisplayList();
    }

    /**
     *  @private
     */
    private var _percentRadius:Number = 50;

    [Bindable("resize")]
    [Inspectable(environment="none")]

    /**
     * Number that specifies the radius length as a percentage
     * of the element's width or height depending on the radiusRelativeTo property.
     * 
     * @default 50
     */
    public function get percentRadius():Number
    {
      return _percentRadius;
    }

    /**
     *  @private
     */
    public function set percentRadius(value:Number):void
    {
      _percentRadius = value;
      if (! isNaN(value))
        _radius = NaN;      
      invalidateDisplayList();
    }

    /**
     *  @private
     */
    private var _originX:Number = NaN;

    [Bindable("originXChanged")]
    [Inspectable(category="Gauge")]
    [PercentProxy("percentOriginX")]

    /**
     * Number that specifies the x position, in pixels, of the origin,
     * in the parent's coordinates.
     *
     * <p><strong>Note:</strong> You can specify a percentage value in the MXML
     * <code>originX</code> attribute, such as <code>originX="48%"</code>,
     * but you cannot use a percentage value in the <code>originX</code>
     * property in ActionScript.
     * Use the <code>percentOriginX</code> property instead.</p>
     * 
     * @default NaN
     */
    public function get originX():Number
    {
      return _originX;
    }

    /**
     *  @private
     */
    public function set originX(value:Number):void
    {
      _originX = value;
      if (! isNaN(value))
        _percentOriginX = NaN;      
      invalidateDisplayList();      
    }

    /**
     *  @private
     */
    private var _originY:Number = NaN;

    [Bindable("originYChanged")]
    [Inspectable(category="Gauge")]
    [PercentProxy("percentOriginY")]

    /**
     * Number that specifies the y position, in pixels, of the origin
     * in the parent's coordinates.
     * 
     * @default NaN 
     */
    public function get originY():Number
    {
      return _originY;
    }
    
    /**
     *  @private
     */
    public function set originY(value:Number):void
    {
      _originY = value;
      if (! isNaN(value))
        _percentOriginY = NaN;      
      invalidateDisplayList();      
    }
    
    /**
     *  @private
     */
    private var _radius:Number = NaN;

    [Bindable("radiusChanged")]
    [Inspectable(category="Gauge")]
    [PercentProxy("percentRadius")]

   /**
    * Number that specifies the radius in pixels.
    *
    * <p><strong>Note:</strong> You can specify a percentage value in the MXML
    * <code>radius</code> attribute, such as <code>radius="48%"</code>,
    * but you cannot use a percentage value in the <code>radius</code>
    * property in ActionScript.
    * Use the <code>percentRadius</code> property instead.</p>
    * 
    * @default NaN
    */    
    public function get radius():Number
    {
      return _radius;
    }

    /**
     *  @private
     */
    public function set radius(value:Number):void
    {
      _radius = value;
      if (! isNaN(value))
        _percentRadius = NaN;      
      invalidateDisplayList();      
    }

    [Inspectable(category="Gauge", enumeration="width, height")]

    /**
     *  @private
     */
    private var _radiusRelativeTo:String = "height";
    
    /**
     * Specifies whether the radius is relative to the width or to the height of the component.
     * 
     * @default "height"
     */    
    public function get radiusRelativeTo():String
    {
      return _radiusRelativeTo;
    }

    /**
     *  @private
     */
    public function set radiusRelativeTo(value:String):void
    {
      _radiusRelativeTo = value;
      invalidateDisplayList();      
    }

    /**
     *  Computes the angle between two radiuses.
     * 
     *  @param startAngle The angle of the first radius.
     *  @param endAngle The angle of the second radius.
     *  @param orientation The orientation. Valid values are "clockwise" and "cclockwise".
     *       
     *  @return An angle, in radians, in the [0, 2*Math.PI] interval.
     */  
    protected function computeAngle(startAngle:Number, endAngle:Number, orientation:String):Number
    {
      var totalAngle:Number;
      
      if (endAngle == startAngle)
        return 2 * Math.PI;
    
      if (orientation == "cclockwise") {
        if (endAngle < startAngle)
          totalAngle = 2 * Math.PI - (startAngle - endAngle);
        else
          totalAngle = endAngle - startAngle;
      } else {
        if (endAngle < startAngle)
          totalAngle = startAngle - endAngle;
        else
          totalAngle = 2 * Math.PI - (endAngle - startAngle);
      }
      return MathUtil.mod2PI(totalAngle);
    }
  }
}
